#include "ddapostplayer.h"
#include "ui_ddapostplayer.h"
#include <QtCore/QCoreApplication>
#include <qtcore/QQueue>
#include <qtcore/QSemaphore>
#include <QtCore/QEventLoop>
#include <QtCore/QMutexLocker>
#include <QtGui/QMouseEvent>
#include <QtCore/QPoint>
#include <QtCore/qtimer.h>
//#include <crtdbg.h>
#ifdef WIN32
#include <direct.h>
#endif

#include "GraphTools.h"
#include "FrameMaker.h"

namespace DDAGui{
    const double TIMEINTERVAL = 200;  // milliseconds time interval between every two frames
    extern DISPLAYMODE playMode ;

    extern void showErrorMessageBox(const string& title , const std::string& text );

    extern void viewFitAll();   // this function is defined in Command.cpp
    extern void sleepQT(int seconds);   // this function is defined in BaseConfigs.h 

	extern void updateRealTimeDisplacement(double value); // in FrameMaker.cpp
	extern bool loadDisplacements(int linesNum); // in FrameMaker.cpp
	extern void addMeasuredPointCoordinate4Idx(int pIdx, double x, double y);
	extern bool loadMeasuredPointCoords(int mpsNum, int framesNum); // in FrameMaker.cpp
	extern void setScale4Display(double scale);
}

using namespace DDAGui;
using namespace std;
//////////////////////////////////////
////  DDAPostplayer
//////////////////////////////////////
DDAPostPlayer::DDAPostPlayer(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::DDAPostPlayer)
{
    ui->setupUi(this);
    _producer = NULL;
    _consumer = NULL;
    initUIConnection();

//     ui->playSlider->installEventFilter(this);

//    _produceThread = new QThread;
//    _consumeThread = new QThread;

//     _producer->moveToThread(_produceThread);
//     _consumer->moveToThread(_consumeThread);
//     _consumeThread->start();
//     _produceThread->start();
// 
}

DDAPostPlayer::~DDAPostPlayer()
{
    delete _producer;
    delete _consumer;
//    _produceThread->terminate();
//    _consumeThread->terminate();
//    _produceThread->wait();
//    _consumeThread->wait();
//    delete _produceThread;
//    delete _consumeThread;
    delete ui;
}

void DDAPostPlayer::initPlay(int framesNum)
{
    ui->frameNoSpinBox->setRange(1 , framesNum);
    ui->frameNoSpinBox->setValue(0);
    ui->playSlider->setRange(1 , framesNum);
    ui->playSlider->setValue(0);
}

void DDAPostPlayer::initUIConnection()
{
    QObject::connect(ui->openBtn , SIGNAL(pressed()) , this , SLOT(openBtnPressed()));
    QObject::connect(ui->playBtn , SIGNAL(pressed()) , this , SLOT(playBtnPressed()));
    QObject::connect(ui->stopBtn , SIGNAL(pressed()) , this , SLOT(stopBtnPressed()));
}

void DDAPostPlayer::initFrameRealtedConnection()
{
//     QObject::connect(_producer , SIGNAL(noMoreFrames()) , _consumer , SLOT(acceptPlayEnd()) , Qt::QueuedConnection);
//     QObject::connect(_producer , SIGNAL(oneFrameProduced()) , _consumer , SLOT(displayFrames()) , Qt::QueuedConnection);
    
//     QObject::connect(_consumer , SIGNAL(frameIndexChanged(int)) , this , SLOT(frameIndexChanged(int)) , Qt::QueuedConnection);
//     QObject::connect(_consumer , SIGNAL(setStartFrameIndex(int)) , _producer , SLOT(setStartFrameIndex(int)) , Qt::QueuedConnection);
//     QObject::connect(_consumer , SIGNAL(requireFrames()) , _producer , SLOT(produceFrames()) , Qt::QueuedConnection);
//     QObject::connect(_consumer , SIGNAL(require1Frame()) , _producer , SLOT(produce1Frame()) , Qt::QueuedConnection);
    
	QObject::connect(ui->scaleSpinBox , SIGNAL(valueChanged(double)) , this , SLOT(scaleValueChanged(double)));
	QObject::connect(ui->frameNoSpinBox , SIGNAL(valueChanged(int)) , this , SLOT(spinBoxValueChanged(int)));
    QObject::connect(ui->frameNoSpinBox , SIGNAL(valueChanged(int)) , ui->playSlider , SLOT(setValue(int)));
    QObject::connect(ui->playSlider , SIGNAL(valueChanged(int)) , this , SLOT(sliderValueChanged(int)));
    QObject::connect(ui->playSlider , SIGNAL(valueChanged(int)) , ui->frameNoSpinBox , SLOT(setValue(int)));
	QObject::connect(ui->radioAccumulatedDisplacement , SIGNAL(toggled(bool)) , this , SLOT(radioButtonAccumulatedDisplacementClicked()));
	QObject::connect(ui->radioRealTimeDisplacement , SIGNAL(toggled(bool)) , this , SLOT(radioButtonRealTimeDisplacementClicked()));
	QObject::connect(ui->radioRealXDisplacement , SIGNAL(toggled(bool)) , this , SLOT(radioButtonRealXDisplacementClicked()));
	QObject::connect(ui->radioRealYDisplacement , SIGNAL(toggled(bool)) , this , SLOT(radioButtonRealYDisplacementClicked()));
    QObject::connect(ui->radioStress , SIGNAL(toggled(bool)) , this , SLOT(radioButtonStressClicked()));

//     QObject::connect(this , SIGNAL(startPlay()) , _consumer , SLOT(startPlay()) , Qt::QueuedConnection);
//     QObject::connect(this , SIGNAL(play1Frame(int)) , _consumer , SLOT(display1Frame(int)) , Qt::QueuedConnection);
//     QObject::connect(this , SIGNAL(stopPlay()) , _consumer , SLOT(stopPlay()) , Qt::QueuedConnection);
//     QObject::connect(this , SIGNAL(pausePlay()) , _consumer , SLOT(pausePlay()) , Qt::QueuedConnection);

    QObject::connect(_producer , SIGNAL(noMoreFrames()) , _consumer , SLOT(acceptPlayEnd()));
    QObject::connect(_producer , SIGNAL(oneFrameProduced()) , _consumer , SLOT(displayFrames()));
 
    QObject::connect(_consumer , SIGNAL(frameIndexChanged(int)) , this , SLOT(frameIndexChanged(int)));
    QObject::connect(_consumer , SIGNAL(setStartFrameIndex(int)) , _producer , SLOT(setStartFrameIndex(int)));
    QObject::connect(_consumer , SIGNAL(requireFrames()) , _producer , SLOT(produceFrames()) );
    QObject::connect(_consumer , SIGNAL(require1Frame()) , _producer , SLOT(produce1Frame()) );
 

    QObject::connect(this , SIGNAL(startPlay()) , _consumer , SLOT(startPlay()) );
    QObject::connect(this , SIGNAL(play1Frame(int)) , _consumer , SLOT(display1Frame(int)) );
    QObject::connect(this , SIGNAL(stopPlay()) , _consumer , SLOT(stopPlay()) );
    QObject::connect(this , SIGNAL(pausePlay()) , _consumer , SLOT(pausePlay()) );
 
}

void DDAPostPlayer::disconnectFrameRealtedConnection()
{
    QObject::disconnect(_consumer , SIGNAL(frameIndexChanged(int)) , this , SLOT(frameIndexChanged(int)));
    QObject::disconnect(this , SIGNAL(startPlay()) , _consumer , SLOT(startPlay()) );
    QObject::disconnect(this , SIGNAL(play1Frame(int)) , _consumer , SLOT(display1Frame(int)) );
    QObject::disconnect(this , SIGNAL(stopPlay()) , _consumer , SLOT(stopPlay()) );
    QObject::disconnect(this , SIGNAL(pausePlay()) , _consumer , SLOT(pausePlay()) );

    QObject::disconnect(_producer , SIGNAL(noMoreFrames()) , _consumer , SLOT(acceptPlayEnd()));
    QObject::disconnect(_producer , SIGNAL(oneFrameProduced()) , _consumer , SLOT(displayFrames()));

    QObject::disconnect(_consumer , SIGNAL(setStartFrameIndex(int)) , _producer , SLOT(setStartFrameIndex(int)));
    QObject::disconnect(_consumer , SIGNAL(requireFrames()) , _producer , SLOT(produceFrames()) );
    QObject::disconnect(_consumer , SIGNAL(require1Frame()) , _producer , SLOT(produce1Frame()) );

}

void DDAPostPlayer::radioButtonAccumulatedDisplacementClicked()
{
    if(ui->radioAccumulatedDisplacement->isChecked())
        playMode = DISPLAY_ACCUMULATION_DISPLACEMENT;
}
void DDAPostPlayer::radioButtonRealTimeDisplacementClicked()
{
	if(ui->radioRealTimeDisplacement->isChecked())
		playMode = DISPLAY_REALTIME_DISPLACEMENT;
}
void DDAPostPlayer::radioButtonRealXDisplacementClicked()
{
	if(ui->radioRealXDisplacement->isChecked())
		playMode = DISPLAY_REAL_X_DISPLACEMENT;
}
void DDAPostPlayer::radioButtonRealYDisplacementClicked()
{
	if(ui->radioRealYDisplacement->isChecked())
		playMode = DISPLAY_REAL_Y_DISPLACEMENT;
}

void DDAPostPlayer::radioButtonStressClicked()
{
    if(ui->radioStress->isChecked())
        playMode = DISPLAY_STRESS;
}

void DDAPostPlayer::playBtnPressed()
{
    if(ui->playBtn->text()==QObject::tr("Play")){
        ui->playBtn->setText(QObject::tr("Pause"));

        ui->openBtn->setEnabled(false);
//         emit startPlay();
        play();
    }
    else{
        ui->openBtn->setEnabled(true);
        ui->playBtn->setText(QObject::tr("Play"));
        emit pausePlay();
    }
}

void DDAPostPlayer::play()
{
    emit startPlay();
    while (_consumer->ifContinuePlay())
    {
        try{
            _producer->produce1Frame();
           }
        catch (std::exception e)
        {
            if( strcmp(e.what(), "Unvalid data")==0)
            {
                handleDataUnValid();
                break;
            }
            else
                throw e;
        }
        QApplication::instance()->processEvents();
        //sleepQT(0.2);
    }
}

void DDAPostPlayer::handleDataUnValid()
{
    showErrorMessageBox(string("DataError"), string("Data format is unvalid. Or maybe data is incomplete."));
    setVisible(false);
}

void DDAPostPlayer::handleDataFileErrorUnValid(char* msg)
{
	showErrorMessageBox(string("DataError"), string(msg));
	setVisible(false);
}


void DDAPostPlayer::openBtnPressed()
{

    int flag = 0;
    if(_producer)
    {
        flag++;
        delete _producer;
        _producer = NULL;
    }
    _producer = new FrameProducer;

    if(_consumer)
    {
        flag++;
        delete _consumer;
        _consumer = NULL;
    }
    assert(flag==0 || flag==2);
    if(flag==2)
        disconnectFrameRealtedConnection();
    _consumer = new FrameConsumer;
    _consumer->setRootSceneNode(_rootScene);
    initFrameRealtedConnection();

    msg("open button pressed.\n");
    char filename[300];
    sprintf(filename , "%s/data.dg" , currentProjectPath);

    try
    {
        if(!_producer->setFile(filename))return;
        FrameDataTaker* dataTaker = _producer->getFrameDataTaker();
        FrameMaker* maker = _consumer->getFrameMaker();

		FrameData& frame = dataTaker->getNextGraph();
		const std::vector<int>& schema = dataTaker->getSchema();

		if(! loadDisplacements(schema[6])) // in FrameMaker.cpp
		{
			char str[300];
			sprintf(str, "File \"%s/displacement.dg\" under project path \
						 is not found or incomplete.", currentProjectPath);
			handleDataFileErrorUnValid(str);
			return;
		}
		if(! loadMeasuredPointCoords(schema[3], schema[6])) // in FrameMaker.cpp
		{
			char str[300];
			sprintf(str, "File \"%s/measuredPointCoordinates.dg\" under project path \
						 is not found or incomplete.", currentProjectPath);
			handleDataFileErrorUnValid(str);
			return;
		}

        maker->reset(frame ,  schema);

    }
	catch( std::out_of_range e)
	{
	}
    catch( std::exception e)
    {
        if( strcmp(e.what(), "Unvalid data")==0)
        {
            handleDataUnValid();
            return;
        }
        else
            throw e;
    }

    ui->playBtn->setEnabled(true);
    ui->stopBtn->setEnabled(true);
    ui->radioAccumulatedDisplacement->setEnabled(true);
	ui->radioRealTimeDisplacement->setEnabled(true);
	ui->radioRealXDisplacement->setEnabled(true);
	ui->radioRealYDisplacement->setEnabled(true);
    ui->radioStress->setEnabled(true);
    ui->playSlider->setEnabled(true);
    ui->frameNoSpinBox->setEnabled(true);
	ui->scaleSpinBox->setEnabled(true);


    initPlay(_producer->getTotalFramesNum());
    viewFitAll();
}

void DDAPostPlayer::stopBtnPressed()
{
    ui->playBtn->setText(QObject::tr("Play"));
    emit stopPlay();
    _producer->setStartFrameIndex(0);
}


void DDAPostPlayer::frameIndexChanged(int frameIndex)
{
    if(frameIndex==1000)
        int a = 5;
    ui->frameNoSpinBox->setValue(frameIndex);
    ui->playSlider->setValue(frameIndex);
    _soQtViewer->render();

    // use signal like this will cause endless recursion, for every signal will call a function
//    if(ui->playBtn->text()==QObject::tr("Pause")) 
//        _producer->produce1Frame();

}

void DDAPostPlayer::spinBoxValueChanged(int frameIndex)
{
    if(ui->frameNoSpinBox->hasFocus())
    {
        ui->playBtn->setText(QObject::tr("Play"));
        ui->frameNoSpinBox->clearFocus();
        msg("display specific frame from spinBox");
        try{
            emit play1Frame(frameIndex);
        }
        catch (std::exception e)
        {
            if( strcmp(e.what(), "Unvalid data")==0)
            {
                handleDataUnValid();
            }
            else
                throw e;
        }
    }
}

void DDAPostPlayer::sliderValueChanged(int frameIndex)
{
    if(ui->playSlider->hasFocus())
    {
        ui->playBtn->setText(QObject::tr("Play"));
        ui->playSlider->clearFocus();
        msg("display specific frame from slider\n");
        try{
            emit play1Frame(frameIndex);
        }
        catch (std::exception e)
        {
            if( strcmp(e.what(), "Unvalid data")==0)
            {
                handleDataUnValid();
            }
            else
                throw e;
        }
    }
}

void DDAPostPlayer::scaleValueChanged(double scale)
{
	setScale4Display(scale);
}

bool DDAPostPlayer::eventFilter(QObject* target , QEvent* event)
{
    if(target == ui->playSlider)
    {
        if(event->type()==QEvent::MouseButtonRelease)
        {
            QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
            if(mouseEvent->buttons() & Qt::LeftButton)
            {
                int duration = ui->playSlider->maximum() - ui->playSlider->minimum();
                double scale = ((double)mouseEvent->x() / ui->playSlider->pos().x())/(double(duration));
                int pos = ui->playSlider->minimum() + int(duration*scale) ;
                ui->playSlider->setValue(pos);     
            }
            return true;
        }
		if(event->type()==QEvent::MouseButtonPress)
			return false;

    }
    return QWidget::eventFilter(target , event);
}
